package jeopardy.server.stats;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import jeopardy.common.protocol.game.stats.GameRec;
import jeopardy.common.protocol.game.stats.ShowGameHistoryReqMsg;
import jeopardy.common.protocol.game.stats.ShowGameHistoryResMsg;
import jeopardy.common.protocol.game.stats.ShowGameListReqMsg;
import jeopardy.common.protocol.game.stats.ShowGameListResMsg;
import jeopardy.common.protocol.game.stats.ShowUserRankingsReqMsg;
import jeopardy.common.protocol.game.stats.ShowUserRankingsResMsg;
import jeopardy.common.protocol.game.stats.UserScore;
import jeopardy.server.ServerConsts;
import jeopardy.server.db.H2ServerDb;

import org.apache.log4j.Logger;

public class StatsManager implements Runnable{

	private static Logger log = Logger.getLogger(StatsManager.class);
	
	private ServerSocket ss;
	private H2ServerDb dbStore;
	
	public StatsManager(ServerSocket ss, H2ServerDb dbStore){
		this.ss = ss;
		this.dbStore = dbStore;
	}
	@Override
	public void run() {
		log.info("Starting Stats Manager on " + ss.getLocalPort());
		
		ThreadPoolExecutor tpe = new ThreadPoolExecutor(
				ServerConsts.SM_CORETHREADS, ServerConsts.SM_POOLSIZE, 
				ServerConsts.SM_THREAD_KEEPALIVE_MILLI, TimeUnit.MILLISECONDS, 
				new LinkedBlockingQueue<Runnable>(ServerConsts.SM_MAX_WAITING_JOBS));
		
		while(ss.isBound() && !ss.isClosed()){
			Socket req = null;
			try{
				req = ss.accept();
				tpe.execute(new StatsServicer(req));
			}catch(IOException ioe){
				log.info("Error accepting connection from client", ioe);
			}catch(RejectedExecutionException ree){
				log.info("Failed to accept more stats request", ree);
				try{
					req.close();
				}catch(Exception e ){
					log.error("task rejected, and socket was not closed.  Turn on debug logging for more info");
					log.debug("task rejected, and socket was not closed.", e);
				}; 
			}catch(Exception e){
				log.error("Unknown exception.  Turn on debug logging for more info");
				log.debug("Error accepting/tasking new account request", e);
			}
		}
	}
	
	private class StatsServicer implements Runnable{
		private Socket s;
		StatsServicer(Socket s){
			this.s = s;
		}
		@Override
		public void run(){
			try{
				s.setSoTimeout(ServerConsts.SM_CONN_KEEPALIVE_MILLI);
			
				ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
				ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
				Object o = ois.readObject();
				
				if(o instanceof ShowGameListReqMsg){
					ShowGameListReqMsg showGameReq = (ShowGameListReqMsg) o;
					String[] playing = null;
					String[] waiting = null;
					if(dbStore.authUser(showGameReq.getUser(), showGameReq.getPass())){
						List<String> pGames = dbStore.getPlayingGames();
						List<String> wGames = dbStore.getWaitingGames();
						
						playing = pGames.toArray(new String[]{});
						waiting = wGames.toArray(new String[]{});
					}
					
					ShowGameListResMsg showGameRes = new ShowGameListResMsg(playing, waiting);
					oos.writeObject(showGameRes);
				}else if(o instanceof ShowUserRankingsReqMsg){
					ShowUserRankingsReqMsg showRanksReq = (ShowUserRankingsReqMsg) o;
					UserScore[] rankers = null;
					if(dbStore.authUser(showRanksReq.getUser(), showRanksReq.getPass())){
						rankers = dbStore.getRankers();
					}
					ShowUserRankingsResMsg res = new ShowUserRankingsResMsg(rankers);
					
					oos.writeObject(res);
				}else if(o instanceof ShowGameHistoryReqMsg){
					ShowGameHistoryReqMsg req = (ShowGameHistoryReqMsg) o;
					GameRec[] games = null;
					if(dbStore.authUser(req.getUser(), req.getPass())){
						games = dbStore.getGames(req.getIdxOfTen());
					}
					ShowGameHistoryResMsg res = new ShowGameHistoryResMsg(games);
					
					oos.writeObject(res);
				}
			
				ois.close();
				oos.close();
			}catch(Exception e){
				log.info("Error stats request");
				log.debug("Error stats request", e);
			}finally{
				try{
					if(s != null && !s.isClosed()){
						s.close();
					}
				}catch(Exception ee){
					log.error("failed to close socket");
					log.debug("failed to close socket", ee);
				}
			}
		}
	}
}
